/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.neuronbit.businessflow.intellij.dialog;

import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiType;
import com.intellij.psi.impl.java.stubs.index.JavaFullClassNameIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTypesUtil;
import com.neuronbit.businessflow.intellij.model.SimpleType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

import static com.neuronbit.businessflow.intellij.dialog.FieldTreeChooseDialog.NodeType.FIELD;
import static com.neuronbit.businessflow.intellij.dialog.FieldTreeChooseDialog.NodeType.OUTPUT;
import static com.neuronbit.businessflow.intellij.util.PsiUtils.getFullTypeName;
import static com.neuronbit.businessflow.intellij.util.PsiUtils.isAtomic;

/**
 * This is dialog box for selecting methods of classes
 */
public class TaskReturnFiledValueAssignToFlowReturnFieldTreeDialog extends FieldTreeChooseDialog {
    private final PsiType psiType;

    /**
     * Instantiates a new Task return filed value assign to flow return field dialog.
     *
     * @param project      the project
     * @param psiType      the psi type
     * @param name         the name
     * @param expectedType the expected type
     */
    public TaskReturnFiledValueAssignToFlowReturnFieldTreeDialog(@Nullable Project project, PsiType psiType, String name, @NotNull PsiType expectedType) {
        super(project, Collections.emptyList(), name, expectedType);
        this.psiType = psiType;
    }


    /**
     * The type My tree node.
     */
    public class MyTreeNode extends FieldTreeNode {

        /**
         * Instantiates a new My tree node.
         *
         * @param project the project
         * @param value   the value
         */
        protected MyTreeNode(Project project, @NotNull NodeValue value) {
            super(project, value);
        }

        @Override
        protected FieldTreeNode createNode(Project project, NodeValue value) {
            return new MyTreeNode(project, value);
        }

        @Override
        public @NotNull Collection<? extends AbstractTreeNode<?>> getChildren() {
            ArrayList<AbstractTreeNode<?>> list = new ArrayList<>();
            NodeType nodeType = this.getValue().getNodeType();
            switch (nodeType) {
                case ROOT -> {
                    PsiClass psiClass = PsiTypesUtil.getPsiClass(psiType);
                    if (isAtomic(psiClass)) {
                        list.add(new MyTreeNode(getProject(), new NodeValue(FIELD, "return", "output", SimpleType.of(getFullTypeName(psiType)))));
                    } else {
                        list.add(new MyTreeNode(getProject(), new NodeValue(OUTPUT, "return", "output", SimpleType.of(getFullTypeName(psiType)))));
                    }
                }
                case OUTPUT -> {
                    Collection<PsiClass> classes = JavaFullClassNameIndex.getInstance()
                            .getClasses(this.getValue().getClazzName().getQualifiedName(), getProject(), GlobalSearchScope.allScope(getProject()));
                    if (classes.isEmpty()) {
                        return list;
                    }
                    for (PsiClass psiClass : classes) {
                        if (isAtomic(psiClass)) {
                            list.add(new MyTreeNode(getProject(), new NodeValue(FIELD, this.getValue().getQualifiedName(), this.getValue().getName(), this.getValue().getClazzName())));
                        } else {
                            Arrays.stream(psiClass.getAllFields())
                                    //.filter(field -> psiClass.findMethodsByName("set" + StringUtils.capitalize(field.getName()), true).length != 0)
                                    .forEach(field -> {
                                        list.add(createNode(getProject(), new NodeValue(FIELD, this.getValue().getQualifiedName(), field.getName(), SimpleType.of(getFullTypeName(field.getType())))));
                                    });
                        }
                    }
                }
                case FIELD -> {
                }
            }
            return list;
        }

    }

    @Override
    public @NotNull Object createRootElement() {
        return new MyTreeNode(getProject(), new NodeValue(TaskReturnFiledValueAssignToFlowReturnFieldTreeDialog.NodeType.ROOT, null, "root object", null));
    }
}
